﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Paxman.Misc;

namespace Paxman
{
    /// <summary>
    /// Objeto que não está fixo na grid
    /// </summary>
    class FreeObject
    {
        public Vector2 position;
        public int idTile;

        public FreeObject(int idTile, Vector2 position)
        {
            this.idTile = idTile;
            this.position = position;
        }
    }

    enum TileType { Empty, Wall, Dot, Energizer, None }

    /// <summary>
    /// Mapa com objetos que o jogador pode colidir
    /// </summary>
    class Map
    {
        /// <summary>Grid de tiles</summary>
        public int[,] grid;
        
        /// <summary>Largura do tile</summary>
        int tileWidth;

        /// <summary>Altura do tile</summary>
        int tileHeight;
        
        /// <summary>Largura do mapa em tiles</summary>
        public int WidthInTiles
        {
            get { return grid.GetLength(0); }
        }

        /// <summary>Altura do mapa em tiles</summary>
        public int HeightInTiles
        {
            get { return grid.GetLength(1); }
        }

        /// <summary>Largura do mapa em pixels</summary>
        public int Width { get; private set; }

        /// <summary>Altura do mapa em pixels</summary>
        public int Height { get; private set; }

        /// <summary>Lista dos objetos de colisão</summary>
        public List<Rectangle> listCollisionObjects;

        /// <summary>Lista dos objetos que não estão fixos a grid</summary>
        public List<FreeObject> listFreeObjects;

        Texture2D txTileset;

        /// <summary>Largura do tileset em tiles</summary>
        public int TilesetWidthInTiles
        {
            get { return txTileset.Width / tileWidth; }
        }

        /// <summary>Altura do tileset em tiles</summary>
        public int TilesetHeightInTiles
        {
            get { return txTileset.Height / tileHeight; }
        }

        public int totalDots;

        /// <summary>Deslocamento adicional em x no momento do Draw</summary>
        private int xOffset = 0;

        /// <summary>Deslocamento adicional em y no momento do Draw</summary>
        private int yOffset = 0;

        /// <summary>Seta um deslocamento no momento do Draw</summary>
        public void SetOffset(int x, int y)
        {
            xOffset = x;
            yOffset = y;
        }

        public void LoadContent(ContentManager content)
        {
            txTileset = content.Load<Texture2D>("tileset");
        }

        /// <summary>Retorna o tipo de tile numa posição da grid</summary>
        /// <param name="xt">Posição x do tile na grid</param>
        /// <param name="yt">Posição y do tile na grid</param>
        public TileType GetTileType(Point tilePos)
        {
            if (tilePos.X > -1 && tilePos.X < WidthInTiles && tilePos.Y > -1 && tilePos.Y < HeightInTiles)
            {
                int i = grid[tilePos.X, tilePos.Y];
                switch(i)
                {
                    case 13:
                        return TileType.Empty;
                    case 19:
                        return TileType.Dot;
                    case 20:
                        return TileType.Energizer;
                    default:
                        return TileType.Wall;
                }    
            }
            else
                return TileType.None;
        }

        public TileType GetTileType(Vector2 position)
        {
            Point tilePos = new Point((int)(position.X / tileWidth), (int)(position.Y / tileHeight));
            return GetTileType(tilePos);
        }

        public TileType GetTileType(int id)
        {
            if ((id > 0 && id < 19) && id != 13)
                return TileType.Wall;

            switch(id)
            {
                case 13:
                    return TileType.Empty;
                case 19:
                    return TileType.Dot;
                case 20:
                    return TileType.Energizer;
                default:
                    return TileType.None;
            } 
        }

        public void ClearTile(Point tilePos)
        {
            if (tilePos.X > -1 && tilePos.X < WidthInTiles && tilePos.Y > -1 && tilePos.Y < HeightInTiles)
                grid[tilePos.X, tilePos.Y] = 13;
        }

        public void ClearTile(Vector2 position)
        {
            Point tilePos = new Point((int)(position.X / tileWidth), (int)(position.Y / tileHeight));
            ClearTile(tilePos);
        }

        public Point TransformInGridPosition(Vector2 position)
        {
            return new Point((int)(position.X / tileWidth), (int)(position.Y / tileHeight));
        }

        /// <summary>
        /// Retorna a posição central de um tile localizado numa posição.
        /// A posição é relativa ao mapa.
        /// TODO: VERIFICAR SE NAO ESTÁ FORA DO MAPA.
        /// </summary>
        public Vector2 GetCenterTileInPosition(Vector2 position)
        {
            Vector2 centerTile;
            centerTile.X = ((int)(position.X / tileWidth) * tileWidth) + (tileWidth / 2);
            centerTile.Y = ((int)(position.Y / tileHeight) * tileHeight) + (tileHeight / 2);

            return centerTile;
        }

        /// <summary>Carrega um mapa</summary>
        /// <param name="numMap">Número do mapa</param>
        public void LoadMap(int numMap)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(@"Maps\map" + numMap + ".tmx");

            // pegando o tamanho dos tiles
            XmlNodeList xnListMap = xmlDoc.GetElementsByTagName("map");
            tileWidth = Convert.ToInt32(xnListMap[0].Attributes["tilewidth"].InnerText);
            tileHeight = Convert.ToInt32(xnListMap[0].Attributes["tileheight"].InnerText);

            // pegando o tamanho da grid e criando a grid
            XmlNodeList xnListLayer = xmlDoc.GetElementsByTagName("layer");
            int w = Convert.ToInt32(xnListLayer[0].Attributes["width"].InnerText);
            int h = Convert.ToInt32(xnListLayer[0].Attributes["height"].InnerText);
            grid = new int[w, h];

            Width = WidthInTiles * tileWidth;
            Height = HeightInTiles * tileHeight;

            // lendo os tiles
            string[] data = xnListLayer[0]["data"].InnerText.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            for (int y = 0; y < h; y++)
            {
                string[] line = data[y].Split(new char[] { ',' });
                for (int x = 0; x < w; x++)
                {
                    grid[x, y] = Convert.ToInt32(line[x]);
                    TileType tileType = GetTileType(grid[x, y]);
                    if (tileType == TileType.Dot || tileType == TileType.Energizer)
                        totalDots++;
                }
            }

            // lendo os objetos de colisão
            XmlNodeList xnListObj = xmlDoc.GetElementsByTagName("object");
            listCollisionObjects = new List<Rectangle>();
            listFreeObjects = new List<FreeObject>();
            foreach (XmlNode xn in xnListObj)
            {
                if (xn.Attributes["type"].InnerText != null)
                {
                    string type = xn.Attributes["type"].InnerText;

                    // quadrados de colisão
                    if (type.CompareTo("rect") == 0)
                    {
                        listCollisionObjects.Add(new Rectangle(
                            Convert.ToInt32(xn.Attributes["x"].InnerText),
                            Convert.ToInt32(xn.Attributes["y"].InnerText),
                            Convert.ToInt32(xn.Attributes["width"].InnerText),
                            Convert.ToInt32(xn.Attributes["height"].InnerText)));
                    }
                    // objetos que não estão fixos na grid
                    else if (type.CompareTo("tile") == 0)
                    {
                        int idTile = Convert.ToInt32(xn.Attributes["gid"].InnerText);
                        Vector2 position = new Vector2(
                            Convert.ToInt32(xn.Attributes["x"].InnerText),
                            Convert.ToInt32(xn.Attributes["y"].InnerText) - tileHeight);
                        listFreeObjects.Add(new FreeObject(idTile, position));
                    }
                }
            }
        }

        /// <summary>Desenha o mapa de tiles</summary>
        public void Draw(SpriteBatch spriteBatch)
        {
            int i;
            Rectangle rectDest = new Rectangle();
            Rectangle rectSource = new Rectangle();

            rectSource.Width = tileWidth;
            rectSource.Height = tileHeight;            
            rectDest.Width = rectSource.Width;
            rectDest.Height = rectSource.Height;

            for (int y = 0; y < HeightInTiles; y++)
            {
                for (int x = 0; x < WidthInTiles; x++)
                {
                    if (grid[x, y] != 0)
                    {
                        i = grid[x, y];

                        // pegando a posição correta na imagem fonte
                        // baseando-se apenas na largura do tileset (em tiles)
                        rectSource.X = ((i - 1) % TilesetWidthInTiles) * tileWidth;
                        rectSource.Y = ((i - 1) / TilesetWidthInTiles) * tileHeight;

                        rectDest.X = (x * tileWidth) + xOffset;
                        rectDest.Y = (y * tileHeight) + yOffset;

                        spriteBatch.Draw(txTileset, rectDest, rectSource, Color.White);
                    }
                }
            }
        }

        /// <summary>Desenha os objetos que não estão fixos a grid</summary>
        public void DrawFreeObjects(SpriteBatch spriteBatch)
        {
            Rectangle rectDest = new Rectangle();
            Rectangle rectSource = new Rectangle();

            rectSource.Width = tileWidth;
            rectSource.Height = tileHeight;
            rectDest.Width = rectSource.Width;
            rectDest.Height = rectSource.Height;

            foreach (FreeObject freeObject in listFreeObjects)
            {
                rectSource.X = ((freeObject.idTile - 1) % TilesetWidthInTiles) * tileWidth;
                rectSource.Y = ((freeObject.idTile - 1) / TilesetWidthInTiles) * tileHeight;

                rectDest.X = (int)freeObject.position.X + xOffset;
                rectDest.Y = (int)freeObject.position.Y + yOffset;

                spriteBatch.Draw(txTileset, rectDest, rectSource, Color.White);
            }
        }
    }
}